//	--------------------------------------------------------------------
//	Copyright(C) 2006,2007 Zhao Yukun. All rights reserved.
//	This file is a part of the Xeres system.
//	Xeres 2007
//	Contact : xeres.engine@gmail.com
//	$(reservedInfo)
//	$(reservedDeclaration)
//	--------------------------------------------------------------------
///	@file	<expose_database_to_python.cpp>
///	@path	~/src/python/wrap/
///	@date	2008/09/28
///	@desc	.

#include "python_pre.h"

#include "lib/utilities/debug.h"

#include "database/database.h"
#include "database/store/IFile.h"
#include "database/store/IFileSystem.h"
#include "database/store/IFileHandler.h"
#include "database/IData.h"
#include "database/DataManager.h"

#include "python/wrap/expose_decl_python.h"
#include "python/core/Include/datetime.h"

namespace xeres {

	namespace
	{
		// Convert date time to python.
		struct DateTime_to_python
		{
			static PyObject* convert( DateTime t )
			{
				// Use datetime module.
				static PyDateTime_CAPI * s_c_api = (PyDateTime_CAPI*) PyCObject_Import( "datetime", "datetime_CAPI" );

				DateTimePartition part;
				t.PartitionDateTime( part );
				return s_c_api->DateTime_FromDateAndTime( 
					part.nYear , part.nMonth , part.nDay , part.nHour , part.nMinute , part.nSecond , 0 ,
					Py_None , s_c_api->DateTimeType );
			}

			static DateTime revert( PyObject * obj )
			{
				static PyDateTime_CAPI * s_c_api = (PyDateTime_CAPI*) PyCObject_Import( "datetime", "datetime_CAPI" );
				if( !PyObject_TypeCheck( obj , s_c_api->DateTimeType ) )
				{
					PyErr_SetString(PyExc_ValueError,
						"Cannot convert object to xeres.DateTime.");
					boost::python::throw_error_already_set();
				}
				// Revert datetime to DateTime.
				DateTimePartition part;
				part.nYear = PyDateTime_GET_YEAR(obj);
				part.nMonth = PyDateTime_GET_MONTH(obj);
				part.nDay = PyDateTime_GET_DAY(obj);
				part.nHour = PyDateTime_DATE_GET_HOUR(obj);
				part.nMinute = PyDateTime_DATE_GET_MINUTE(obj);
				part.nSecond = PyDateTime_DATE_GET_SECOND(obj);
				DateTime t;
				t.FromPartition( part );
				return t;
			}
		};
		//////////////////////////////////////////////////////////////////////////
		// IData wrap.

		std::string IDateEntryRepr( IData * entry )
		{
			char buf[256];
			sprintf( buf , "<xeres.%S object (key:%S | child:%u)>" , entry->GetClassName().c_str() , 
				entry->GetKey().GetCStr() , entry->GetChildCount() );
			return buf;
		}

		template<
			typename _Type
		> object ReadValueWrap1( IData * entry , const WString& path )
		{
			_Type value;
			Ref<IData> child = entry->OpenData( path );
			if( child.IsValid() && AccessEntry<_Type>::Get( child , value ) )
				return object( value );
			else
				return object();
		}

		template<
			typename _Type
		> object ReadValueWrap2( IData * entry , const WString& path , const _Type& defaultVal )
		{
			_Type value;
			Ref<IData> child = entry->OpenData( path );
			if( child.IsValid() && AccessEntry<_Type>::Get( child , value ) )
				return object( value );
			else
				return object( defaultVal );
		}

		template<
			typename _Type
		> boost::python::list ReadSeqWrap( IData * entry , const WString& path )
		{
			boost::python::list lst;
			std::vector<_Type> seq;
			AccessEntry<_Type>::GetArray( entry , path , seq );
			for( size_t i = 0 , total = seq.size() ; i < total ; ++i )
				lst.append( seq[i] );
			return lst;
		}
		template<
			typename _Type
		> boost::python::list ReadSeqByKeyWrap( IData * entry , const Key& key )
		{
			boost::python::list lst;
			std::vector<_Type> seq;
			AccessEntry<_Type>::GetArray( entry , key , seq );
			for( size_t i = 0 , total = seq.size() ; i < total ; ++i )
				lst.append( seq[i] );
			return lst;
		}

		struct DataInfoAccess
		{
			static PyObject * modified( DataInfo * info ) { return DateTime_to_python::convert( info->m_modified ); }
			static PyObject * created( DataInfo * info ) { return DateTime_to_python::convert( info->m_created ); }
			static PyObject * accessed( DataInfo * info ) { return DateTime_to_python::convert( info->m_accessed ); }
		};

		struct DataInfoWrite
		{
			static void modified( DataInfo * info , PyObject * obj ) { info->m_modified = DateTime_to_python::revert( obj ); }
			static void created( DataInfo * info , PyObject * obj ) { info->m_created = DateTime_to_python::revert( obj ); }
			static void accessed( DataInfo * info , PyObject * obj ) { info->m_accessed = DateTime_to_python::revert( obj ); }
		};

		object IDataEntryGetAttributeWrap1( IData * entry , const WString& attr )
		{
			WString value;
			if( entry->GetAttribute( attr , value ) )
				return object( value );
			else
				return object();
		}

		object IDataEntryGetAttributeWrap2( IData * entry , const WString& attr , const WString& defaultVal )
		{
			WString value;
			if( entry->GetAttribute( attr , value ) )
				return object( value );
			else
				return object( defaultVal );
		}

		Ref<IData> IDataEntryNewChildWrap( IData * entry , const Key& key )
		{
			return entry->NewChild( key );
		}

		object IDataEntryIndexOfWrap( IData * entry , IData * child )
		{
			size_t index;
			if( entry->IndexOf( child , index ) )
				return object( index );
			else
				return object();
		}

		bool IDataEntryDeleteChildWrap1( IData * entry , size_t index )
		{
			return entry->DeleteChild( index );
		}

		bool IDataEntryDeleteChildWrap2( IData * entry , IData * child )
		{
			return entry->DeleteChild( child );
		}

		bool IDataEntryDeleteChildWrap3( IData * entry , const Key& key )
		{
			return entry->DeleteChild( key );
		}

		void IDataEntryDeleteChildrenWrap1( IData * entry )
		{
			entry->DeleteChildren();
		}

		void IDataEntryDeleteChildrenWrap2( IData * entry , const Key& key )
		{
			entry->DeleteChildren( key );
		}

		Ref<IData> IDataEntryGetChildWrap1( IData * entry , const Key& key )
		{
			return entry->GetChild( key );
		}

		Ref<IData> IDataEntryGetChildWrap2( IData * entry , size_t index )
		{
			return entry->GetChild( index );
		}

		Ref<IData> IDataEntryGetChildWrap3( IData * entry , const WString& name )
		{
			return entry->GetChild( name );
		}

		object IDataEntryGetChildInfoWrap1( IData * entry , const Key& key )
		{
			DataInfo info;
			if( entry->GetChildInfo( key , info ) )
				return object( info );
			else
				return object();
		}

		object IDataEntryGetChildInfoWrap2( IData * entry , const WString& name )
		{
			DataInfo info;
			if( entry->GetChildInfo( name , info ) )
				return object( info );
			else
				return object();
		}

		object IDataEntryGetChildInfoNameWrap( IData * entry , size_t index )
		{
			DataInfo info;
			WString name;
			if( entry->GetChildInfo( index , info , name ) )
				return boost::python::make_tuple( name , info );
			else
				return object();
		}
		
		object IDataEntryFindChildIndexWrap1( IData * entry , const Key& key )
		{
			size_t index;
			if( entry->FindChildIndex( key , index ) )
				return object( index );
			else
				return object();
		}

		object IDataEntryFindChildIndexWrap2( IData * entry , const WString& name )
		{
			size_t index;
			if( entry->FindChildIndex( name , index ) )
				return object( index );
			else
				return object();
		}

		object IDataEntryFindChildWrap1( IData * entry , const Key& key )
		{
			size_t index;
			Ref<IData> retval( entry->FindChild( key , index ) );
			if( retval.IsValid() )
				return boost::python::make_tuple( retval , index );
			else
				return object();
		}

		object IDataEntryFindChildWrap2( IData * entry , const WString& name )
		{
			size_t index;
			Ref<IData> retval( entry->FindChild( name , index ) );
			if( retval.IsValid() )
				return boost::python::make_tuple( retval , index );
			else
				return object();
		}

		object IDataEntryOpenEntryWrap1( IData * entry , const WString& path )
		{
			Ref<IData> retval( entry->OpenData( path ) );
			if( retval.IsValid() )
				return object( retval );
			else
				return object();
		}

		object IDataEntryOpenEntryWrap2( IData * entry , const WString& path , bool create_non_exist )
		{
			Ref<IData> retval( entry->OpenData( path , create_non_exist ) );
			if( retval.IsValid() )
				return object( retval );
			else
				return object();
		}

		boost::python::list IDataEntryOpenEntriesWrap( IData * entry , const WString& path )
		{
			boost::python::list lst;
			std::vector< Ref<IData> > result;
			entry->OpenDataArray( path , result );
			for( size_t i = 0 , total = result.size() ; i < total ; ++i )
				lst.append( result[i] );
			return lst;
		}

		//////////////////////////////////////////////////////////////////////////
		// Child iterator.
		class IDataEntryIter
		{
		public:
			/// \ctor
			IDataEntryIter( RefWeak<IData> entry ) : m_entry( entry ) , m_index( 0 ) {}

			/// \ctor
			IDataEntryIter( const IDataEntryIter& rhs ) : m_entry( rhs.m_entry ) , m_index( rhs.m_index ) {}

			/// \next
			object next( void )
			{
				Ref<IData> child;
				do
				{
					if( m_index >= m_entry->GetChildCount() )
					{
						PyErr_SetString( PyExc_StopIteration , "No more data." );
						boost::python::throw_error_already_set();
						return object();
					}
					child = m_entry->GetChild( m_index );
					++m_index;
				}while( child.IsNull() );
				return object( make_tuple( boost::python::str( child->GetKey().GetString() ) , object( child ) ) );
			}

		private:

			// @data
			Ref<IData> m_entry;
			size_t m_index;
		};

		IDataEntryIter IDataEntryGenChildIter( IData * entry )
		{
			return IDataEntryIter( entry );
		}

		//////////////////////////////////////////////////////////////////////////
		// Attribute iterator.
		class AttributeIterator
		{
		public:
			/// \ctor
			AttributeIterator( RefWeak<IData> entry ) : m_entry( entry ) , m_context( entry->AttributeBegin() ) {}

			/// \ctor
			AttributeIterator( const AttributeIterator& rhs ) :m_entry( rhs.m_entry ) , m_context( rhs.m_context ) {}

			/// \next
			object next( void )
			{
				WString name , attr;
				if( !m_entry->AttributeIterate( m_context , name , attr , true ) )
				{
					PyErr_SetString( PyExc_StopIteration , "No more data." );
					boost::python::throw_error_already_set();
					return object();
				}
				return object( make_tuple( name , attr ) );
			}

		private:

			// @data
			void * m_context;
			Ref<IData> m_entry;
		};

		AttributeIterator AttributeGenIter( IData * entry )
		{
			return AttributeIterator( entry );
		}

		//////////////////////////////////////////////////////////////////////////
		// Key conversion.

		Key * make_key( const WString& val )
		{
			return new Key( Key::MakeKey( val ) );
		}

		WString from_key( Key * key )
		{
			return key->GetString();
		}
	}

	void expose_xres( void )
	{
		//////////////////////////////////////////////////////////////////////////
		// Basic structure.
		class_< DataInfo >( "DataInfo" , init<>() )
			.def_readwrite( "type" , &DataInfo::m_type )
			.def_readwrite( "size" , &DataInfo::m_size )
			.add_property( "modified" , &DataInfoAccess::modified , &DataInfoWrite::modified )
			.add_property( "created" , &DataInfoAccess::created , &DataInfoWrite::created )
			.add_property( "accessed" , &DataInfoAccess::accessed , &DataInfoWrite::accessed )
		;

		class_< Key >( "Key" , no_init )
			.def( "__init__" , boost::python::make_constructor( &make_key ) )
			// Operators.
			.def( "__str__" , boost::python::make_function( &from_key ) );
		;

		// make Key-String convertible
		boost::python::implicitly_convertible< WString , Key >();
		boost::python::implicitly_convertible< Key , WString >();

		//////////////////////////////////////////////////////////////////////////
		// Elements.

		boost::python::iterator_wrappers<IDataEntryIter>::wrap("IDataEntryIterator");
		boost::python::iterator_wrappers<AttributeIterator>::wrap("IDataEntryAttrIterator");
		class_< IData , bases< IVar > , boost::noncopyable >( "IData" , no_init )
			// Object property.
			.add_property( "children" , &IDataEntryGenChildIter )
			.add_property( "attributes" , &AttributeGenIter )
			// Methods.
			.def( "GetInfo" , &IData::GetInfo )
			.def( "GetKey" , &IData::GetKey , RET_COPYREF )
			.def( "GetAttribute" , &IDataEntryGetAttributeWrap1 )
			.def( "GetAttribute" , &IDataEntryGetAttributeWrap2 )
			.def( "SetAttribute" , &IData::SetAttribute )
			// Data access.
			.def( "ReadBool" , &ReadValueWrap1<bool> )
			.def( "ReadBool" , &ReadValueWrap2<bool> )
			.def( "ReadInt" , &ReadValueWrap1<int> )
			.def( "ReadInt" , &ReadValueWrap2<int> )
			.def( "ReadFloat" , &ReadValueWrap1<float> )
			.def( "ReadFloat" , &ReadValueWrap2<float> )
			.def( "ReadPair" , &ReadValueWrap1< Pair<float> > )
			.def( "ReadPair" , &ReadValueWrap2< Pair<float> > )
			.def( "ReadVector" , &ReadValueWrap1< Vector<float> > )
			.def( "ReadVector" , &ReadValueWrap2< Vector<float> > )
			.def( "ReadQuat" , &ReadValueWrap1< Quat<float> > )
			.def( "ReadQuat" , &ReadValueWrap2< Quat<float> > )
			.def( "ReadColor" , &ReadValueWrap1< Color4 > )
			.def( "ReadColor" , &ReadValueWrap2< Color4 > )
			.def( "ReadMatrix" , &ReadValueWrap1< Matrix<float> > )
			.def( "ReadMatrix" , &ReadValueWrap2< Matrix<float> > )
			.def( "ReadString" , &ReadValueWrap1< WString > )
			.def( "ReadString" , &ReadValueWrap2< WString > )
			// FIXME ReadBuffer

			.def( "WriteBool" , &IData::WriteBool )
			.def( "WriteInt" , &IData::WriteInt )
			.def( "WriteFloat" , &IData::WriteFloat )
			.def( "WritePair" , &IData::WritePair )
			.def( "WriteVector" , &IData::WriteVector )
			.def( "WriteQuat" , &IData::WriteQuat )
			.def( "WriteColor" , &IData::WriteColor )
			.def( "WriteMatrix" , &IData::WriteMatrix )
			.def( "WriteString" , &IData::WriteString )
			// FIXME WriteBuffer

			// Array
			.def( "ReadBools" , &ReadSeqWrap<bool> )
			.def( "ReadInts" , &ReadSeqWrap<int> )
			.def( "ReadPairs" , &ReadSeqWrap< Pair<float> > )
			.def( "ReadVectors" , &ReadSeqWrap< Vector<float> > )
			.def( "ReadQuats" , &ReadSeqWrap< Quat<float> > )
			.def( "ReadColors" , &ReadSeqWrap< Color4 > )
			.def( "ReadMatrices" , &ReadSeqWrap< Matrix<float> > )
			.def( "ReadStrings" , &ReadSeqWrap< WString > )

			.def( "ReadBools" , &ReadSeqByKeyWrap<bool> )
			.def( "ReadInts" , &ReadSeqByKeyWrap<int> )
			.def( "ReadPairs" , &ReadSeqByKeyWrap< Pair<float> > )
			.def( "ReadVectors" , &ReadSeqByKeyWrap< Vector<float> > )
			.def( "ReadQuats" , &ReadSeqByKeyWrap< Quat<float> > )
			.def( "ReadColors" , &ReadSeqByKeyWrap< Color4 > )
			.def( "ReadMatrices" , &ReadSeqByKeyWrap< Matrix<float> > )
			.def( "ReadStrings" , &ReadSeqByKeyWrap< WString > )

			// Hierarchy.
			.def( "AddChild" , &IData::AddChild )
			.def( "NewChild" , &IDataEntryNewChildWrap )
			.def( "IndexOf" , &IDataEntryIndexOfWrap )
			.def( "DeleteChild" , &IDataEntryDeleteChildWrap1 )
			.def( "DeleteChild" , &IDataEntryDeleteChildWrap2 )
			.def( "DeleteChild" , &IDataEntryDeleteChildWrap3 )
			.def( "DeleteChildren" , &IDataEntryDeleteChildrenWrap1 )
			.def( "DeleteChildren" , &IDataEntryDeleteChildrenWrap2 )
			.def( "GetChildCount" , &IData::GetChildCount )
			.def( "GetChild" , &IDataEntryGetChildWrap1 )
			.def( "GetChild" , &IDataEntryGetChildWrap2 )
			.def( "GetChild" , &IDataEntryGetChildWrap3 )
			.def( "GetChildInfo" , &IDataEntryGetChildInfoWrap1 )
			.def( "GetChildInfo" , &IDataEntryGetChildInfoWrap2 )
			.def( "GetChildInfoName" , &IDataEntryGetChildInfoNameWrap )
			.def( "FindChildIndex" , &IDataEntryFindChildIndexWrap1 )
			.def( "FindChildIndex" , &IDataEntryFindChildIndexWrap2 )
			.def( "FindChild" , &IDataEntryFindChildWrap1 )
			.def( "FindChild" , &IDataEntryFindChildWrap2 )
			.def( "OpenData" , &IDataEntryOpenEntryWrap1 )
			.def( "OpenData" , &IDataEntryOpenEntryWrap2 )
			.def( "OpenDataArray" , &IDataEntryOpenEntriesWrap )
			// Operators.
			.def( "__repr__" , &IDateEntryRepr )
		;
		register_ptr_to_python< Ref<IData> >();
		register_object_to_python<IData>();

	}

	namespace
	{
#define __declspec(X)

		BOOST_PYTHON_MODULE( xeres_xres )
		{
			expose_xres();
		}

#undef __declspec
	}

	// expose database class
	void expose_database_to_python( DataManager * dataman )
	{
		PythonPlug::instance().RegisterModule( "xeres" , init_module_xeres_xres );

		// Register root entry.
		try
		{
			using namespace boost::python::converter;
			PythonPlug::instance().RegisterObject( "xeres" , "root" ,
				object( Ref<IData>( dataman->OpenRoot() ) ) );
		}
		catch ( boost::python::error_already_set )
		{
			PyErr_Print();
			PyErr_Clear();
		}
	}

} // namespace xeres
